package com.meida.bpm.provider.controller;

/**
 * 流程定义控制器
 */

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.google.common.collect.ImmutableMap;
import com.meida.bpm.provider.common.BaseFlowableController;
import com.meida.bpm.provider.constant.FlowableConstant;
import com.meida.bpm.provider.handler.ProcDefListHandler;
import com.meida.bpm.provider.model.ProcessDefinitionRequest;
import com.meida.bpm.provider.model.ProcessDefinitionResponse;
import com.meida.bpm.provider.service.ProcessDefinitionService;
import com.meida.common.base.entity.EntityMap;
import com.meida.common.mybatis.model.ResultBody;
import com.meida.common.security.OpenHelper;
import com.meida.common.utils.ApiAssert;
import com.meida.common.base.utils.FlymeUtils;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.flowable.common.engine.api.query.QueryProperty;
import org.flowable.engine.impl.ProcessDefinitionQueryProperty;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 流程定义控制器
 *
 * @author zyf
 */
@RestController
@RequestMapping("/bpm/processDefinition")
@RequiredArgsConstructor
public class ProcessDefinitionController extends BaseFlowableController {
    private static final Map<String, QueryProperty> ALLOWED_SORT_PROPERTIES = new HashMap<>();

    private final ProcessDefinitionService processDefinitionService;

    static {
        ALLOWED_SORT_PROPERTIES.put(FlowableConstant.ID, ProcessDefinitionQueryProperty.PROCESS_DEFINITION_ID);
        ALLOWED_SORT_PROPERTIES.put(FlowableConstant.KEY, ProcessDefinitionQueryProperty.PROCESS_DEFINITION_KEY);
        ALLOWED_SORT_PROPERTIES.put(FlowableConstant.CATEGORY, ProcessDefinitionQueryProperty.PROCESS_DEFINITION_CATEGORY);
        ALLOWED_SORT_PROPERTIES.put(FlowableConstant.NAME, ProcessDefinitionQueryProperty.PROCESS_DEFINITION_NAME);
        ALLOWED_SORT_PROPERTIES.put(FlowableConstant.VERSION, ProcessDefinitionQueryProperty.PROCESS_DEFINITION_VERSION);
        ALLOWED_SORT_PROPERTIES.put(FlowableConstant.TENANT_ID, ProcessDefinitionQueryProperty.PROCESS_DEFINITION_TENANT_ID);
    }

    /**
     * 流程列表
     *
     * @param params
     * @return
     */
    @GetMapping(value = "/list")
    public ResultBody list(@RequestParam(required = false) Map params) {
        EntityMap entityMap = new EntityMap(params);
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        String id = entityMap.get(FlowableConstant.ID);
        String category = entityMap.get(FlowableConstant.CATEGORY);
        String key = entityMap.get(FlowableConstant.KEY);
        String name = entityMap.get(FlowableConstant.NAME);
        if (FlymeUtils.isNotEmpty(id)) {
            query.processDefinitionId(id);
        }
        if (FlymeUtils.isNotEmpty(category)) {
            query.processDefinitionCategoryLike(FlymeUtils.convertToLike(category));
        }
        if (FlymeUtils.isNotEmpty(key)) {
            query.processDefinitionKeyLike(FlymeUtils.convertToLike(key));
        }
        if (FlymeUtils.isNotEmpty(name)) {
            query.processDefinitionNameLike(FlymeUtils.convertToLike(name));
        }
        return pageList(entityMap, query, ProcDefListHandler.class, ALLOWED_SORT_PROPERTIES, null);
    }

    /**
     * 查询我能发起的流程
     *
     * @param params
     * @return
     */
    @GetMapping(value = "/listMyself")
    public ResultBody listMyself(@RequestParam(required = false) Map params) {
        EntityMap entityMap = new EntityMap(params);
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        if (ObjectUtils.isNotEmpty(params.get(FlowableConstant.NAME))) {
            processDefinitionQuery.processDefinitionNameLike(FlymeUtils.convertToLike(entityMap.get(FlowableConstant.NAME)));
        }
        processDefinitionQuery.latestVersion().active().startableByUser(OpenHelper.getStrUserId());
        return pageList(params, processDefinitionQuery, ProcDefListHandler.class, ALLOWED_SORT_PROPERTIES, null);
    }

    /**
     * 获取流程详细信息
     *
     * @param processDefinitionId
     * @return
     */
    @GetMapping(value = "/queryById")
    public ResultBody queryById(@RequestParam String processDefinitionId) {
        String userId = OpenHelper.getStrUserId();
        //校验权限
        permissionService.validateReadPermissionOnProcessDefinition(userId, processDefinitionId);
        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinitionById(processDefinitionId);
        String formKey = null;
        if (processDefinition.hasStartFormKey()) {
            formKey = formService.getStartFormKey(processDefinitionId);
        }
        ProcessDefinitionResponse processDefinitionResponse = responseFactory.createProcessDefinitionResponse(processDefinition, formKey);
        return ResultBody.ok(processDefinitionResponse);
    }

    /**
     * 初始化流程表单
     *
     * @param processDefinitionId
     * @return
     */
    @GetMapping(value = "/renderedStartForm")
    public ResultBody renderedStartForm(@RequestParam String processDefinitionId) {
        //校验权限
        permissionService.validateReadPermissionOnProcessDefinition(OpenHelper.getStrUserId(), processDefinitionId);
        Object renderedStartForm = formService.getRenderedStartForm(processDefinitionId);
        boolean showBusinessKey = this.isShowBusinessKey(processDefinitionId);
        return ResultBody.ok(ImmutableMap.of("renderedStartForm", renderedStartForm, "showBusinessKey", showBusinessKey));
    }


    /**
     * 流程图
     *
     * @param processDefinitionId
     * @return
     */
    @GetMapping(value = "/image")
    public ResultBody image(@RequestParam String processDefinitionId, HttpServletResponse response) {
        //校验权限
        permissionService.validateReadPermissionOnProcessDefinition(OpenHelper.getStrUserId(), processDefinitionId);
        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinitionById(processDefinitionId);
        InputStream imageStream = repositoryService.getProcessDiagram(processDefinition.getId());
        if (imageStream == null) {
            ApiAssert.failure("流程未定义");
        }
        byte[] bytes = new byte[0];
        try {
            bytes = IOUtils.toByteArray(imageStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String encoded = Base64.getEncoder().encodeToString(bytes);
        return ResultBody.ok(encoded);
    }

    /**
     * 查看流程xml
     *
     * @param processDefinitionId
     * @return
     */
    @GetMapping(value = "/xml")
    public ResponseEntity<byte[]> xml(@RequestParam String processDefinitionId) {
        permissionService.validateReadPermissionOnProcessDefinition(OpenHelper.getStrUserId(), processDefinitionId);
        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinitionById(processDefinitionId);
        String deploymentId = processDefinition.getDeploymentId();
        String resourceId = processDefinition.getResourceName();
        if (deploymentId == null || deploymentId.length() == 0) {
            ApiAssert.failure("流程未部署deploymentId：" + deploymentId);
        }
        if (resourceId == null || resourceId.length() == 0) {
            ApiAssert.failure("流程资源未定义resourceId：" + resourceId);
        }
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
        if (deployment == null) {
            ApiAssert.failure("流程未部署");
        }

        List<String> resourceList = repositoryService.getDeploymentResourceNames(deploymentId);
        if (ObjectUtils.isEmpty(resourceList) || !resourceList.contains(resourceId)) {
            ApiAssert.failure("流程部署异常");
        }
        InputStream resourceStream = repositoryService.getResourceAsStream(deploymentId, resourceId);
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.setContentType(MediaType.TEXT_XML);
        try {
            return new ResponseEntity<>(IOUtils.toByteArray(resourceStream), responseHeaders, HttpStatus.OK);
        } catch (Exception e) {
            ApiAssert.failure("获取流程定义XML信息异常");
        }
        return null;
    }

    /**
     * 删除流程定义
     *
     * @param params
     * @return
     */
    @PostMapping(value = "/delete")
    public ResultBody delete(@RequestParam(required = false) Map params) {
        processDefinitionService.delete(params);
        return ResultBody.msg("删除成功");
    }

    /**
     * 激活流程
     *
     * @param actionRequest
     * @return
     */
    @PutMapping(value = "/activate")
    public ResultBody activate(@RequestBody ProcessDefinitionRequest actionRequest) {
        processDefinitionService.activate(actionRequest);
        return ResultBody.msg("激活成功");
    }

    /**
     * 挂起流程
     *
     * @param actionRequest
     * @return
     */
    @PutMapping(value = "/suspend")
    public ResultBody suspend(@RequestBody ProcessDefinitionRequest actionRequest) {
        processDefinitionService.suspend(actionRequest);
        return ResultBody.msg("挂起成功");
    }

    /**
     * 导入流程定义
     *
     * @param request
     * @return
     */
    @PostMapping(value = "/import")
    public ResultBody doImport(@RequestParam(required = false) String tenantId, HttpServletRequest request) {
        processDefinitionService.importProcess(tenantId, request);
        return ResultBody.msg("导入成功");
    }

}
